// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: wsfuyibing <682805@qq.com>
// Date: 2024-08-20

package gen_annotation

import (
	"bufio"
	"fmt"
	"os"
	"regexp"
	"strings"
)

var (
	ResourceRegexMatchAnnotation        = regexp.MustCompile(`^/+\s*@([_a-zA-Z0-9]+)\(?([^)]*)\)?`)
	ResourceRegexMatchComment           = regexp.MustCompile(`^/+\s?(.*)`)
	ResourceRegexMatchPackageFromImport = regexp.MustCompile(`([_a-zA-Z0-9]+)$`)

	// ResourceRegexMatchController
	// is a regular expression to match controller type.
	//
	//   func ExampleMiddleware(i iris.Context) {
	//       ...
	//   }
	ResourceRegexMatchController     = regexp.MustCompile(`^([_a-zA-Z0-9]*Controller)\s+struct\s*\{`)
	ResourceRegexMatchController2    = regexp.MustCompile(`^type\s+([_a-zA-Z0-9]*Controller)\s+struct\s*\{`)
	ResourceRegexMatchControllerName = regexp.MustCompile(`^[A-Z][_a-zA-Z0-9]*$`)

	// ResourceRegexMatchHandler
	// is a regular expression to match handler function.
	//
	//   func ExampleHandler(i iris.Context) {
	//       ...
	//   }
	ResourceRegexMatchHandler = regexp.MustCompile(`^func\s+([A-Z][_a-zA-Z0-9]*Handler)\s*\([_a-zA-Z0-9]*\s*iris\.Context\)\s+\{`)

	// ResourceRegexMatchCrontab
	// is a regular expression to match crontab type.
	//
	//   func ExampleMiddleware(i iris.Context) {
	//       ...
	//   }
	ResourceRegexMatchCrontab  = regexp.MustCompile(`^([A-Z][_a-zA-Z0-9]*Crontab)\s+struct\s*\{`)
	ResourceRegexMatchCrontab2 = regexp.MustCompile(`^type\s+([A-Z][_a-zA-Z0-9]*Crontab)\s+struct\s*\{`)

	// ResourceRegexMatchMiddleware
	// is a regular expression to match middleware function.
	//
	//   func ExampleMiddleware(i iris.Context) {
	//       ...
	//   }
	ResourceRegexMatchMiddleware = regexp.MustCompile(`^func\s+([A-Z][_a-zA-Z0-9]*Middleware)\s*\([_a-zA-Z0-9]*\s*iris\.Context\)\s+\{`)

	ResourceRegexRemoveFilename = regexp.MustCompile(`/([^/]+)$`)
)

type (
	// Resource
	// is a component used to translate source code file.
	Resource struct {
		Folder string // folder
		Path   string // app/middlewares
		Src    string // example_middleware.go

		// Import
		// is an application import package for scanned source file.
		//
		//   gitee.com/go-wares/framework-iris/_examples/framework/app/crontabs
		//   gitee.com/go-wares/framework-iris/_examples/framework/app/middlewares
		Import string

		// Package
		// is end word of Import field.
		//
		//   crontabs
		//   middlewares
		Package string
	}

	ResourceScanner interface {
		Match(resource *Resource, comment *Comment, line int, text string) (err error)
	}
)

// NewResource
// creates a resource with given path and source.
func NewResource(path, src string) *Resource {
	return &Resource{
		Path: path, Src: src,
	}
}

// FillSrc
// fill prefix of src field.
func (o *Resource) FillSrc(folders ...string) {
	if len(folders) > 0 {
		o.Folder = strings.Join(folders, "/")
		o.Src = o.Folder + "/" + o.Src
	}
}

// Reader
// read content of the resource.
func (o *Resource) Reader(provider *Provider, scanner ResourceScanner) (err error) {
	// Build
	// import path.
	o.Import = fmt.Sprintf(`%s/%s`, provider.Module, provider.ModuleFolder) + "/" + ResourceRegexRemoveFilename.ReplaceAllString(fmt.Sprintf(`%s/%s`, o.Path, o.Src), "")

	// Built
	// import package of path.
	if m := ResourceRegexMatchPackageFromImport.FindStringSubmatch(o.Import); len(m) > 0 {
		o.Package = m[1]
	}

	// Return error
	// if package name parse failed.
	if o.Package == "" {
		err = fmt.Errorf("package name parse failed: %s/%s", o.Path, o.Src)
		return
	}

	// Prepare read file.
	var (
		comment *Comment
		file    *os.File
		line    int
		src     = fmt.Sprintf(`%s/%s/%s`, provider.WorkingDir, o.Path, o.Src)
		text    string
	)

	// Read source file.
	if file, err = os.OpenFile(src, os.O_RDONLY, os.ModePerm); err != nil {
		err = fmt.Errorf("open file failed: %v", err)
		return
	}

	// Close
	// file after use.
	defer func() {
		_ = file.Close()
	}()

	// File
	// content scanner.
	sc := bufio.NewScanner(file)

	// Range
	// with line by line.
	for sc.Scan() {
		line++

		// Continue to do
		// if current line is blank.
		if text = strings.TrimSpace(sc.Text()); text == "" {
			if comment != nil {
				comment = nil
			}
			continue
		}

		// Comment
		// found then parse it.
		if ResourceRegexMatchComment.MatchString(text) {
			if comment == nil {
				comment = NewComment()
			}

			comment.Add(text)
			continue
		}

		// Source parse.
		if comment == nil {
			comment = NewComment()
		}
		if err = scanner.Match(o, comment, line, text); err != nil {
			break
		}
		comment = nil
	}
	return
}
